##############
# (*) Kaleidoscope
# By: Allan Spale
# June 2008

from __future__ import with_statement

import sys
import uuid

from Kaleidoscope.KSColumn import KSColumn
from Kaleidoscope.KSUtility import KSUtility
from Kaleidoscope.KSEventManager import KSEventManager
from Kaleidoscope.KSTransaction import KSTransaction
from Kaleidoscope.KSQueryItem import KSQueryItem


class KSTableError(Exception):
    """
    Exception class for KSTable

    """
    B_UNKNOWN_EXCEPTION = True
    B_CLASS_EXCEPTION = False


    def __init__(self, u_message):
        self.__u_message = u_message
    def __str__(self):
        return repr(self.__u_message)

    @staticmethod
    def print_and_raise_error(u_message, b_unknown_exception):
        """
        Convenience method for printing and raising an error for this
        particular Exception subclass.

        """
        #sys.stderr.write(sys.exc_info())  # write initial error
        u_message = u'KSTable.py: ' + u_message
        sys.stderr.write(u_message + u'\n')

        if (b_unknown_exception):
            raise Exception(u_message)
        else:
            raise KSTableError(u_message)


class KSTable(object):
    """
    This class is a mostly abstract case that will handle the creation,
    manipulation, and deletion of columns in a table. The primary key column
    will be the provider of the transaction factory. Please note, all of the
    columns for this table must reside in the same datastore environment.

    NOTE: Probably will want to add EventManager to this class.

    """
    @staticmethod
    def empty_function():
        """Used as an empty function as needed."""
        pass

    @property
    def u_name(self):
        return self.__u_table_name

    @property
    def is_open(self):
        return self.__b_open


    def __init__(self, u_table_name):
        try:
            self.__b_open = False

            if (isinstance(u_table_name, unicode)):
                #if (KSUtility.type_check([unicode], [u_table_name])):
                self.__u_table_name = u_table_name
                self.__ks_primary_column = None
                self.__id = uuid.uuid4()
                self.__dict_columns = {}
                self.__dict_queries = {}
                self.get_transaction = None

                self.__ksem_event_manager = KSEventManager(self.__id)
                self.__ksem_event_manager.register_all_functions_from_class(
                    KSTable, 200)
                self.__context = KSUtility(
                    self.__ksem_event_manager, KSTable.empty_function)
            else:
                KSTableError.print_and_raise_error(u'A unicode name must '
                    + u'be provided as the name of the table.',
                    KSTableError.B_UNKNOWN_EXCEPTION)

        except Exception:
            KSTableError.print_and_raise_error(u'An internal error '
                + u'prevented KSTablefrom being initialized.',
                KSTableError.B_UNKNOWN_EXCEPTION)


    def __del__(self):
        """
        Upon deletion of the class instance, close the column files.

        """
        self.close()


    def open(self, i_flags, i_permissions, kstx_parent_transaction):
        """
        Open the database files for the columns.

        """
        with self.__context.complete_transaction(kstx_parent_transaction,
            self.open, [int, int, KSTransaction], [i_flags, i_permissions,
            kstx_parent_transaction]) as kstx_transaction:

            if not (self.__b_open):
                print 'opening stuff...', self.__dict_columns
                for u_column_name in self.__dict_columns:
                    self.__dict_columns[u_column_name].open(
                        i_flags, i_permissions, kstx_transaction)
                    print 'opened %s: %s' \
                        % (u_column_name, self.__dict_columns[u_column_name])
                # end loop
                self.__b_open = True

            self.__ksem_event_manager.send_after_event_message(
                self.open, [self.__b_open])
            return self.__b_open


    def close(self):
        """
        Close the database files for the columns.

        """
        with self.__context.send_before_event(self.close, []):

            if (self.__b_open):
                for u_column_name in self.__dict_columns:
                    self.__dict_columns[u_column_name].close()
                self.__b_open = False

        self.__ksem_event_manager.send_after_event_message(
            self.close, [self.__b_open])
        return self.__b_open


    def add_column(self, u_column_name, ks_column):
        """
        Add a column to the table. This is the column that will determine which
        keys are a part of the other table columns, even if the other columns
        have keys that this column does not have.

        """
        try:
            if (self.__context.type_check_and_send_event(self.add_column,
                [unicode, KSColumn], [u_column_name, ks_column])):

                self.__dict_columns[u_column_name] = ks_column

            self.__ksem_event_manager.send_after_event_message(
                self.add_column, [True])
            return True

        except KeyError:
            KSTableError.print_and_raise_error(u'The column name %s already'
                + u'exists as an identifier for a column in this table.'
                % u_column_name, KSTableError.B_CLASS_EXCEPTION)


    def rename_column(self, u_old_column_name, u_new_column_name):
        """
        Change the column name.

        """
        try:
            if (self.__context.type_check_and_send_event(self.rename_column,
                [unicode, unicode], [u_old_column_name, u_new_column_name])):

                ks_column = self.__dict_columns[u_old_column_name]
                del self.__dict_columns[u_old_column_name]
                self.__dict_columns[u_new_column_name] = ks_column

            self.__ksem_event_manager.send_after_event_message(
                self.rename_column, [True])
            return True

        except KeyError:
            KSTableError.print_and_raise_error(u'The column name %s already'
                + u'exists as an identifier for a column in this table.'
                % u_new_column_name, KSTableError.B_CLASS_EXCEPTION)


    def remove_column(self, u_column_name):
        """
        Remove the column from the table.

        """
        try:
            if (KSUtility.type_check_and_send_event(self.remove_column,
                [unicode], [u_column_name])):

                del self.__dict_columns[u_column_name]

            self.__ksem_event_manager.send_after_event_message(
                self.remove_column, [True])
            return True

        except KeyError:
            KSTableError.print_and_raise_error(u'The column name %s could'
                + u'not be removed from the table.' % u_column_name,
                KSTableError.B_CLASS_EXCEPTION)


    def get_column(self, u_column_name):
        """
        Retrieve the KSColumn object from the name.

        """
        try:
            if (self.__context.type_check_and_send_event(self.get_column,
                [unicode], [u_column_name])):

                result = self.__dict_columns[u_column_name]

            self.__ksem_event_manager.send_after_event_message(
                self.get_column, [result])
            return result

        except KeyError:
            KSTableError.print_and_raise_error(u'The column name %s could'
                + u'not be found in the table.' % u_column_name,
                KSTableError.B_CLASS_EXCEPTION)


    def set_primary_column(self, u_column_name):
        """
        Set the primary column by listing the name of an existing column
        already present in KSTable.

        """
        if (self.__context.type_check_and_send_event(self.set_primary_column,
            [unicode], [u_column_name])):

            self.__ks_primary_column = self.__dict_columns[u_column_name]
            self.get_transaction = \
                self.__ks_primary_column.get_transaction
            self.__context = KSUtility(
                self.__ksem_event_manager, self.get_transaction)

        self.__ksem_event_manager.send_after_event_message(
            self.set_primary_column, [True])
        return True


    def change_name(self, u_name):
        """
        Change the name of the table.

        """
        if (isinstance(u_name, unicode)):
            self.__u_table_name = u_name
            return True
        else:
            KSTableError.print_and_raise_error(
                u"""An error occurred while attempting to change the name of
                table to %s.""" % u_name, KSTableError.B_UNKNOWN_EXCEPTION)


    def add_row(self, obj_key, dict_fields_and_values, kstx_parent_transaction):
        """
        Add a new item to each of the columns based on the field names
        listed in the dictionary. Because there is no predefined order of
        columns, it is permissible to have missing data for a given field.
        Although the missing data will not be added to that column for
        this key value, any aggregate data operations will treat this data
        as missing for that key value.

        It is important to note that columns which support multiple values
        because it is permissible for a list of values to be assigned for
        a particular value.

        """
        try:
            with self.__context.complete_transaction(kstx_parent_transaction,
                self.add_row, [object, dict, KSTransaction],
                [obj_key, dict_fields_and_values, kstx_parent_transaction]) \
                    as kstx_transaction:

                for u_column_name in dict_fields_and_values:
                    if (isinstance(
                        dict_fields_and_values[u_column_name], list)):
                        #print '>>> before insert into %s' %u_column_name
                        #print self.__dict_columns[u_column_name].print_all(kstx_transaction)
                        self.__dict_columns[
                            u_column_name].insert_items_with_key_and_value_list(
                                obj_key, dict_fields_and_values[u_column_name],
                                kstx_transaction)
                        #print '*** after...'
                        #print self.__dict_columns[u_column_name].print_all(kstx_transaction)

                    else:
                        #print '>>> before insert into %s' %u_column_name
                        #print self.__dict_columns[u_column_name].print_all(kstx_transaction)
                        self.__dict_columns[
                            u_column_name].insert_item_with_key_value_pair(
                                obj_key, dict_fields_and_values[u_column_name],
                                kstx_transaction)
                        #print '*** after...'
                        #print self.__dict_columns[u_column_name].print_all(kstx_transaction)

            self.__ksem_event_manager.send_after_event_message(
                self.add_row, [True])
            return True

        except KeyError:
            KSTableError.print_and_raise_error(u'The column name %s could'
                + u' not be found in the table.' % u_column_name,
                KSTableError.B_CLASS_EXCEPTION)


    def change_row(self, obj_key, dict_fields_and_values,
                       kstx_parent_transaction):
        """
        Change an existing tuple with a key for a new item to each of the
        columns based on the field names listed in the dictionary. Not all
        columns have to be updated or even listed in this dictionary. Columns
        with multiple values are not allowed to be updated using this
        operation. It is safer to do a deletion and insertion to allow the
        programmer to be aware of how this operation actually works.

        """
        try:
            with self.__context.complete_transaction(kstx_parent_transaction,
                self.change_row, [object, dict, KSTransaction],
                [obj_key, dict_fields_and_values, kstx_parent_transaction]) \
                    as kstx_transaction:

                for u_column_name in dict_fields_and_values:
                    if (isinstance(
                        dict_fields_and_values[u_column_name], list)):
                        KSTableError.print_and_raise_error(u'The column name '
                            + u'%s cannot be updated because columns that '
                            + u'allow multiple values cannot be updated with '
                            + u'this operation.' % u_column_name,
                            KSTableError.B_CLASS_EXCEPTION)
                    else:
                        self.__dict_columns[
                            u_column_name].change_key_value_pair(obj_key,
                                dict_fields_and_values[u_column_name],
                                kstx_transaction)

            self.__ksem_event_manager.send_after_event_message(
                self.change_row, [True])
            return True

        except KeyError:
            KSTableError.print_and_raise_error(u'The column name %s could'
                + u' not be found in the table.' % u_column_name,
                KSTableError.B_CLASS_EXCEPTION)


    def get_row(self, obj_key, list_u_fields, kstx_parent_transaction):
        """
        Retrieve a row using the given key. Only include the columns in
        the list that are given by name in the second parameter.

        """
        with self.__context.complete_transaction(kstx_parent_transaction,
            self.get_row, [object, list, KSTransaction],
            [obj_key, list_u_fields, kstx_parent_transaction]) \
                as kstx_transaction:

            kstx_transaction = self.get_transaction(kstx_parent_transaction,
                self.get_row)
            list_results = []

            for u_column_name in list_u_fields:
                try:
                    ks_column = self.__dict_columns[u_column_name]
                    stuff = ks_column.get_data_from_key(
                        obj_key, kstx_transaction, KSColumn.B_REGULAR_DATA)
                    list_results.append(stuff)

                except KeyError:
                    list_results.append(KSColumn.NULL_VALUE)
                    continue

                except Exception:
                    list_results.append(KSColumn.NULL_VALUE)
                    continue

        self.__ksem_event_manager.send_after_event_message(
            self.add_column, [list_results])
        return list_results


    def get_row_by_primary_value(self, obj_value, list_u_fields,
                                     kstx_parent_transaction):
        """
        Acts just like get_row but give the value of the primary key rather
        than the actual numerical key.

        """
        with self.__context.complete_transaction(kstx_parent_transaction,
            self.get_row_by_primary_value, [object, list, KSTransaction],
            [obj_value, list_u_fields, kstx_parent_transaction]) \
                as kstx_transaction:

            # Get the key from the value, then call get_row
            obj_key = self.__get_key_from_primary_column_val(
                obj_value, kstx_transaction)

            if (obj_key != None):
                list_results = self.get_row(obj_key, list_u_fields,
                                            kstx_transaction)
            else:
                return None

        self.__ksem_event_manager.send_after_event_message(
            self.get_row_by_primary_value, [list_results])
        return list_results


    def delete_row_by_primary_value(self, obj_value,
                                     kstx_parent_transaction):
        """
        Acts just like delete_row but give the value of the primary key rather
        than the actual numerical key.

        """
        with self.__context.complete_transaction(kstx_parent_transaction,
            self.delete_row_by_primary_value, [object, KSTransaction],
            [obj_value, kstx_parent_transaction]) as kstx_transaction:

            # Get the key from the value, then call get_row
            obj_key = self.__get_key_from_primary_column_val(
                obj_value, kstx_transaction)

            result = self.delete_row(obj_key, kstx_transaction)

        self.__ksem_event_manager.send_after_event_message(
            self.delete_row_by_primary_value, [result])
        return result


    def delete_row(self, obj_key, kstx_parent_transaction):
        """
        For ALL COLUMNS in this table, delete the specified keyed item.

        """
        with self.__context.complete_transaction(kstx_parent_transaction,
            self.delete_row, [object, KSTransaction],
            [obj_key, kstx_parent_transaction]) as kstx_transaction:

            for u_column_name in self.__dict_columns:
                if (self.__dict_columns.has_key(u_column_name)):
                    self.__dict_columns[u_column_name].\
                        delete_item_by_key(obj_key, kstx_transaction)

        self.__ksem_event_manager.send_after_event_message(
            self.delete_row, [True])
        return True


    def delete_partial_row(self, obj_key, list_u_fields,
                               kstx_parent_transaction):
        """
        Delete the data with obj_key for the given columns listed in
        list_u_fields.

        """
        with self.__context.complete_transaction(kstx_parent_transaction,
            self.delete_partial_row, [object, KSTransaction],
            [obj_key, kstx_parent_transaction]) as kstx_transaction:

            for u_column_name in list_u_fields:
                ks_column = self.__dict_columns[u_column_name]
                if (ks_column.has_key(obj_key)):
                    ks_column.delete_item_by_key(obj_key, kstx_transaction)
                # else, nothing
                # This would be the equivalent of NULL, just ignore the
                # absence of the key for this particular column

        self.__ksem_event_manager.send_after_event_message(
            self.delete_partial_row, [True])
        return True


    def size(self, kstx_parent_transaction):
        """
        Since columns can vary in the number of rows, just calculate the
        number of items in the primary column.

        """
        with self.__context.complete_transaction(kstx_parent_transaction,
            self.size, [KSTransaction], [kstx_parent_transaction]) \
                as kstx_transaction:

            result = self.__ks_primary_column.size(kstx_transaction)

        self.__ksem_event_manager.send_after_event_message(
            self.size, [result])
        return result


    def add_query(self, u_query_name, u_column_name, u_fn_query_name,
                      list_parameters):
        """
        Create a KSQueryItem for the specified column name. Just like with
        KSQueryItem, supply the fn_name and parameter list.

        """
        try:
            if (self.__context.type_check_and_send_event(self.add_query,
                [unicode, unicode, unicode, list], [u_query_name, u_column_name,
                u_fn_query_name, list_parameters])):

                if not (self.__dict_queries.has_key(u_query_name)):
                    if (self.__dict_columns.has_key(u_column_name)):
                        self.__dict_queries[u_query_name] = \
                            KSQueryItem(self.__dict_columns[u_column_name],
                                u_fn_query_name, list_parameters)
                    else:
                        KSTableError.print_and_raise_error(u'The column named '
                            + u'"%s" does not exist in the table.' \
                            % u_query_name, KSTableError.B_CLASS_EXCEPTION)
                else:
                    KSTableError.print_and_raise_error(u'The query named "%s" '
                        + u'already exists in the table.' % u_query_name,
                        KSTableError.B_CLASS_EXCEPTION)

            self.__ksem_event_manager.send_after_event_message(
                self.add_query, [True])
            return True

        except Exception:
            KSTableError.print_and_raise_error(u'The query could not be added '
                + u'to the table.' % u_query_name,
                KSTableError.B_CLASS_EXCEPTION)


    def change_parameter_list(self, u_query_name, list_parameters):
        """
        Wrapper function for the KSQueryItem by the same name.

        """
        try:
            if (self.__context.type_check_and_send_event(
                self.change_parameter_list, [unicode, list],
                [u_query_name, list_parameters])):

                if (self.__dict_queries.has_key(u_query_name)):
                    self.__dict_queries[u_query_name].change_parameter_list(
                        list_parameters)
                else:
                    KSTableError.print_and_raise_error(u'The query named "%s" '
                        + u'does not exist in the table.' % u_query_name,
                        KSTableError.B_CLASS_EXCEPTION)

            self.__ksem_event_manager.send_after_event_message(
                self.change_parameter_list, [True])
            return True

        except Exception:
            KSTableError.print_and_raise_error(u'The query %s was unable to '
                + u'change its parameter list.' % u_query_name,
                KSTableError.B_CLASS_EXCEPTION)


    def change_query(self, u_query_name, u_column_name,
                         u_fn_query_name, list_parameters):
        """
        Change the properties of the KSQuery object with the given name.

        """
        try:
            if (self.__context.type_check_and_send_event(self.change_query,
                [unicode, unicode, unicode, list], [u_query_name, u_column_name,
                u_fn_query_name, list_parameters])):

                if (self.__dict_queries.has_key(u_query_name)):
                    if (self.__dict_columns.has_key(u_column_name)):
                        self.__dict_queries[u_query_name] = \
                            KSQueryItem(self.__dict_columns[u_column_name],
                                u_fn_query_name, list_parameters)
                    else:
                        KSTableError.print_and_raise_error(u'The column named '
                            + u'"%s" does not exist in the table.' \
                            % u_query_name, KSTableError.B_CLASS_EXCEPTION)
                else:
                    KSTableError.print_and_raise_error(u'The query named "%s" '
                        + u'does not exist in the table.' % u_query_name,
                        KSTableError.B_CLASS_EXCEPTION)

            self.__ksem_event_manager.send_after_event_message(
                self.change_query, [True])
            return True

        except Exception:
            KSTableError.print_and_raise_error(u'The existing query %s could '
                + u'be changed for this table.' % u_query_name,
                KSTableError.B_CLASS_EXCEPTION)


    def remove_query(self, u_query_name):
        """
        Remove the query with the given name.

        """
        try:
            if (self.__context.type_check_and_send_event(self.remove_query,
                [unicode], [u_query_name])):

                del self.__dict_queries[u_query_name]

            self.__ksem_event_manager.send_after_event_message(
                self.remove_query, [True])
            return True

        except KeyError:
            KSTableError.print_and_raise_error(u'The column name %s could'
                + u' not be found in the table.' % u_query_name,
                KSTableError.B_CLASS_EXCEPTION)


    def has_key(self, obj_key, kstx_parent_transaction):
        """
        Check if the key exists for the primary column. Logically, this same
        key should exist for the other columns in this table, though it is
        possible that they do not because there is no appropriate value
        assignment for a particular column in the table.

        """
        with self.__context.complete_transaction(kstx_parent_transaction,
            self.has_key, [object, KSTransaction], [obj_key,
                kstx_parent_transaction]) as kstx_transaction:

            result = self.__ks_primary_column.has_key(
                obj_key, kstx_transaction)

        self.__ksem_event_manager.send_after_event_message(
            self.has_key, [result])
        return result


    def has_value(self, u_column_name, obj_value, kstx_parent_transaction):
        """
        Because getting a key that does not exist will result in a canceled
        transaction, it is necessary to check for a value and simply receive
        a boolean result. For this reason, send a column name and a value and
        see if it exists.

        """
        with self.__context.complete_transaction(kstx_parent_transaction,
            self.has_value, [unicode, object, KSTransaction], [u_column_name,
                obj_value, kstx_parent_transaction]) as kstx_transaction:

            b_result = self.__dict_columns[u_column_name].has_value(
                obj_value, kstx_transaction)

        self.__ksem_event_manager.send_after_event_message(
            self.has_value, [b_result])
        return b_result


    def keys(self, kstx_parent_transaction):
        """
        Return the keys from the primary column of the table.

        """
        try:
            with self.__context.complete_transaction(kstx_parent_transaction,
                self.keys, [KSTransaction],
                [kstx_parent_transaction]) as kstx_transaction:

                list_keys = self.__ks_primary_column.keys(
                    kstx_transaction, KSColumn.B_REGULAR_DATA)

            self.__ksem_event_manager.send_after_event_message(
                self.keys, [list_keys])
            return list_keys
        except Exception:
            KSTableError.print_and_raise_error(u'The keys for the table could '
                + u'not be retrieved.', KSTableError.B_CLASS_EXCEPTION)


    def values(self, u_column_name, kstx_parent_transaction):
        """
        Return the values from the column named u_column_name in the table.

        """
        try:
            with self.__context.complete_transaction(kstx_parent_transaction,
                self.values, [unicode, KSTransaction],
                [u_column_name, kstx_parent_transaction]) as kstx_transaction:

                ks_column = self.__dict_columns[u_column_name]
                list_values = ks_column.values(
                    kstx_transaction, KSColumn.B_REGULAR_DATA)

            self.__ksem_event_manager.send_after_event_message(
                self.values, [list_values])
            return list_values
        except Exception:
            KSTableError.print_and_raise_error(u'The values for the column '
                + u'named %s not be retrieved because of an unknown error.'
                % (u_column_name), KSTableError.B_CLASS_EXCEPTION)


    def run_once_query(self, u_query_name, kstx_parent_transaction):
        """
        Submit a KSQuery object that will individually query each column and
        collect the results based on the operators among the column subqueries.
        There is no "planning" involved with optimizing queries for speed or
        efficiency. This will have to wait for a future version. Individual
        fields can be selected rather than viewing an entire table. Depending
        upong the query type, the result will behave either like a series of
        KSTable commands.

        """
        try:
            with self.__context.complete_transaction(kstx_parent_transaction,
                self.run_once_query, [unicode, KSTransaction],
                [u_query_name, kstx_parent_transaction]) as kstx_transaction:

                ksq_query = self.__dict_queries[u_query_name]
                ksq_query.execute_operation(kstx_transaction)
                #ksq_query.execute_operation(b_use_cached_result)
                obj_result = ksq_query.get_result_set()

            self.__ksem_event_manager.send_after_event_message(
                self.run_once_query, [obj_result])
            return obj_result

        except KeyError:
            KSTableError.print_and_raise_error(u'The query named "%s" could'
                + u' not be found in the table.' % u_query_name,
                KSTableError.B_CLASS_EXCEPTION)


    def flush_ongoing_query_results(self, u_query_name):
        """
        If the queries have not been completed on all of the columns, this
        will force the query to complete executing for this snapshot in time.
        Return the results from the query and then clear its key cache. After
        the cache for this query is flushed, future updates that occur to each
        of the columns, will slowly rebuild the result.

        """
        try:
            if (self.__context.type_check_and_send_event(
                self.flush_ongoing_query_results, [unicode], [u_query_name])):

                self.__dict_queries[u_query_name].clear_results()

            self.__ksem_event_manager.send_after_event_message(
                self.flush_ongoing_query_results, [True])
            return True

        except KeyError:
            KSTableError.print_and_raise_error(u'The query named "%s" could'
                + u' not be found in the table.' % u_query_name,
                KSTableError.B_CLASS_EXCEPTION)


    def generate_row_id(self):
        """
        Create a unique row ID. Do not send an event because this is a
        trivial function that does not need to be announced.

        """
        try:
            return self.__ks_primary_column.generate_id()

        except Exception:
            KSTableError.print_and_raise_error(u'Please define specify a '
                + u'primary column before attempting to generate a row ID.',
                KSTableError.B_CLASS_EXCEPTION)


    def property_function(self, u_column_name, u_op_string):
        """
        Format: +~-> (+=add, ~=change, -=remove, >=get)
        Use . if the function should not be applied
        Generates add_propname, change_propname, remove_propname, get_propname

        """
        if (self.__context.type_check_and_send_event(self.property_function,
            [unicode, unicode], [u_column_name, u_op_string])):

            if (u_op_string[0] == u'+'):
                self.__dict__[u'add_'+ u_column_name] = \
                    lambda x, y: self.__add_property_data(x, y, u_column_name)

            if (u_op_string[1] == u'~'):
                self.__dict__[u'change_'+ u_column_name] = lambda x, y, z: \
                    self.__change_property_data(x, y, z, u_column_name)

            if (u_op_string[2] == u'-'):
                self.__dict__[u'remove_'+ u_column_name] = lambda x, y: \
                    self.__remove_property_data(x, y, u_column_name)

            if (u_op_string[3] == u'>'):
                self.__dict__[u'get_'+ u_column_name] = lambda x, y: \
                    self.__get_property_data(x, y, u_column_name)

        print 'dictionary of functions: '
        print self.__dict__

        self.__ksem_event_manager.send_after_event_message(
            self.property_function, [])


    def print_all(self, kstx_parent_transaction):
        """
        Print the contents of the table.

        """
        print
        if (self.__ks_primary_column.size(kstx_parent_transaction) != 0):
            print 'Contents of table', self.__u_table_name
            print '[key, ',
            for u_name in self.__dict_columns:
                print u_name + ',',
            print ']'
            print '=========================================================='

            list_fields = self.__dict_columns.keys()
            for obj_key in self.__ks_primary_column.keys(
                kstx_parent_transaction, KSColumn.B_REGULAR_DATA):

                print obj_key, \
                    self.get_row(obj_key, list_fields, kstx_parent_transaction)

            print
        else:
            print
            print '%s: >>> no entries <<<' % self.__u_table_name
            print



    def __add_property_data(self, obj_data, kstx_parent_transaction,
                                u_column_name):
        """
        Dynamic method that will be called by a lambda function with respect
        to a given table column name. This function will add obj_data to
        the column named u_column_name.

        """
        try:
            with self.__context.complete_transaction(kstx_parent_transaction,
                self.__add_property_data, [object, KSTransaction, unicode],
                [obj_data, kstx_parent_transaction, u_column_name]) \
                    as kstx_transaction:

                i_id = self.generate_row_id()
                self.add_row(i_id, {self.__dict_columns[u_column_name]:
                    obj_data}, kstx_transaction)

            self.__ksem_event_manager.send_after_event_message(
                self.__add_property_data, [i_id])
            return i_id

        except Exception:
            KSTableError.print_and_raise_error(u'An internal error '
                + u'prevented the data from being added to %s.' \
                % u_column_name, KSTableError.B_UNKNOWN_EXCEPTION)


    def __change_property_data(self, obj_key, obj_data,
                                   kstx_parent_transaction, u_column_name):
        """
        Dynamic method that will be called by a lambda function with respect
        to a given table column name. This function will change the data
        stored in obj_key to obj_data in the column named u_column_name.

        """
        try:
            with self.__context.complete_transaction(kstx_parent_transaction,
                self.__change_property_data, [object, object, KSTransaction,
                unicode], [obj_key, obj_data, kstx_parent_transaction,
                u_column_name]) as kstx_transaction:

                self.change_row(obj_key, {self.__dict_columns[u_column_name]:
                    obj_data}, kstx_transaction)

            self.__ksem_event_manager.send_after_event_message(
                self.__change_property_data, [True])
            return True

        except Exception:
            KSTableError.print_and_raise_error(u'An internal error '
                + u'prevented the data from being changed in %s.' \
                % u_column_name, KSTableError.B_UNKNOWN_EXCEPTION)


    def __remove_property_data(self, obj_key, kstx_parent_transaction,
                                   u_column_name):
        """
        Dynamic method that will be called by a lambda function with respect
        to a given table column name. This function will remove the data
        stored in obj_key in the column named u_column_name.

        """
        try:
            with self.__context.complete_transaction(kstx_parent_transaction,
                self.__remove_property_data, [object, KSTransaction, unicode],
                [obj_key, kstx_parent_transaction, u_column_name]) \
                    as kstx_transaction:

                self.__dict_columns[u_column_name].delete_item_by_key(
                    obj_key, kstx_transaction)

            self.__ksem_event_manager.send_after_event_message(
                self.__remove_property_data, [True])
            return True

        except Exception:
            KSTableError.print_and_raise_error(u'An internal error '
                + u'prevented the data from being removed in %s.' \
                % u_column_name, KSTableError.B_UNKNOWN_EXCEPTION)


    def __get_property_data(self, obj_key, kstx_parent_transaction,
                                u_column_name):
        """
        Dynamic method that will be called by a lambda function with respect
        to a given table column name. This function will retrieve the data
        stored in obj_key in the column named u_column_name.

        """
        try:
            with self.__context.complete_transaction(kstx_parent_transaction,
                self.__get_property_data, [object, KSTransaction, unicode],
                [obj_key, kstx_parent_transaction, u_column_name]) \
                    as kstx_transaction:

                list_data = self.get_row(obj_key,[u_column_name],
                    kstx_transaction)

                # If only one item is expected, just return the item by itself;
                # otherwise, return the list as is.
                if (len(list_data) == 1):
                    obj_data = list_data[0]
                else:
                    obj_data = list_data

            self.__ksem_event_manager.send_after_event_message(
                self.__get_property_data, [obj_data])
            return obj_data

        except Exception:
            KSTableError.print_and_raise_error(u'An internal error '
                + u'prevented the location from being retrieved.',
                KSTableError.B_UNKNOWN_EXCEPTION)


    def __get_key_from_primary_column_val(self, obj_value, kstx_transaction):
        """
        Retrieve the key from the primary column value.

        """
        list_keys = self.__ks_primary_column.get_keys_from_data_value(
            obj_value, kstx_transaction, KSColumn.B_REGULAR_DATA)

        if (len(list_keys)==1):
            return list_keys.pop()
        else:
            return None
            #KSTableError.print_and_raise_error(u'The value %s for the '
            #    + u'primary column is not unique.' % obj_value,
            #    KSTableError.B_CLASS_EXCEPTION)
